package org.amse.korovkin.ciphering.model;

import java.io.FileNotFoundException;
import org.amse.korovkin.ciphering.model.cipher.monocipher.MonoAttacker;
import org.amse.korovkin.ciphering.model.cipher.monocipher.MonoCipher;
import java.io.IOException;
import java.util.*;
import java.util.zip.DataFormatException;
import org.amse.korovkin.ciphering.model.cipher.ICipher;
import org.amse.korovkin.ciphering.model.cipher.monocipher.ChangeTable;
import org.amse.korovkin.ciphering.model.cipher.splinewavelet.Spline3WaveletCipher;
import org.amse.korovkin.ciphering.model.cipher.splinewavelet.Spline1WaveletCipher;
import org.amse.korovkin.ciphering.model.cipher.splinewavelet.SplineKey;
import org.amse.korovkin.ciphering.view.*;

public class Model implements IModel{

    private static IModel model= null;
    private ArrayList<IView> listener = new ArrayList<IView>();
    private static final Map<String, ICipher> cipherMap = new HashMap<String, ICipher>();
    private String cipherMode;

    public static final int ATTACK_JDIALOG = 1;
    public static final String cipher1 = Spline3WaveletCipher.getInstance().getName();
    public static final String cipher2 = Spline1WaveletCipher.getInstance().getName();
    public static final String cipher3 = MonoCipher.getInstance().getName();

    private Model() {
        initCipherMap();
    }

    public static IModel getInstance() {
        if (model == null) {
            model = new Model();
        }
        return model;
    }
    public void encrypt(String inFile, String outFile, String keyFile)
            throws FileNotFoundException, IOException {
        getCipher().encrypt(inFile, outFile, keyFile);
    }

    public void decrypt(String inFile, String outFile, String keyFile)
            throws FileNotFoundException, IOException {
        getCipher().decrypt(inFile, outFile, keyFile);
    }

    public int attack(String inFile, String outFile)
            throws IOException, DataFormatException, InterruptedException {
        getCipher().attack(inFile, outFile);
        return MonoAttacker.attack(inFile, outFile);
    }

    public void createEtalonStatistic(String inFile)
            throws IOException, DataFormatException {
        Statistic.createEtalonStatistic(inFile);
    }
    public void setStopFlag(boolean flag){
        MonoAttacker.setStopFlag(flag);
    }
    public void setPauseFlag(boolean flag){
        MonoAttacker.setPauseFlag(flag);
    }
    public String getMomentFilePath() {
        return MonoAttacker.getMomentFilePath();
    }
    public int getProgress() {
        return MonoAttacker.getProgress();
    }
    public void setTrueChoices(char[] letters)
            throws DataFormatException, InterruptedException {
        MonoAttacker.setTrueChoices(letters);
    }

    public void refresh(int key) {
        switch (key) {
            case ATTACK_JDIALOG: {
                for (Iterator it  = listener.iterator(); it.hasNext();) {
                    Object list = it.next();
                    if (list.getClass() == AttackJDialog.class) {
                        ((AttackJDialog)list).update();
                    }
                }
            }
        }
    }

    public void addListener(IView view) {
        listener.add(view);
    }

    public void removeListener(IView view) {
        listener.remove(view);
    }

    public ICipher getCipher() {
        return cipherMap.get(cipherMode);
    }
    
    public void setCipher(String cipherName) {
        cipherMode = cipherName;
    }

    public void generateRandomKey() {
        // check size of keys
        if (cipherMode.equals(cipher1)) {
            SplineKey.generate(Spline3WaveletCipher.getInstance().getBlockSize() -
                    0, Spline3WaveletCipher.getInstance().getBlockSize() - 4);
        } else if (cipherMode.equals(cipher2)) {
            SplineKey.generate(Spline1WaveletCipher.getInstance().getBlockSize() +
                    1, Spline3WaveletCipher.getInstance().getBlockSize() - 2);
        } else if (cipherMode.equals(cipher3)) {
            ChangeTable.generate();
        } else {
            throw new NullPointerException("Ciphermode was not initialised.");
        }
    }

    private void initCipherMap() {
        cipherMap.put(cipher1, Spline3WaveletCipher.getInstance());
        cipherMap.put(cipher2, Spline1WaveletCipher.getInstance());
        cipherMap.put(cipher3, MonoCipher.getInstance());
    }
}
